using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;

namespace TestUtil.Equality
{
    /// <summary>
    /// Helps with testing the hash code generation of <typeparamref name="TUnderTest"/> matches our expectations.
    /// </summary>
    /// <remarks>
    /// This class expects that if two objects are equal they have the same hash code and if the hash code method is called
    /// repeatedly it will produce the same results.
    /// </remarks>
    public sealed class HashCodeTestHelper<TUnderTest>
    {
        #region Fields

        private CreateTestObjectCallback<TUnderTest> _createTestObjectCallback;
        
        #endregion

        #region Constructor

        /// <summary>
        /// Creates an instance of this class.
        /// </summary>
        /// <param name="createTestObjectCallback">Creates instances of <typeparamref name="TUnderTest"/> on demand.</param>
        public HashCodeTestHelper(CreateTestObjectCallback<TUnderTest> createTestObjectCallback)
        {
            _createTestObjectCallback = createTestObjectCallback;
        }
        
        #endregion

        #region Public Methods

        /// <summary>
        /// Asserts that the hash code behavior for <typeparamref name="TUnderTest"/> matches our expections.
        /// </summary>
        public void PerformTest()
        {
            AssertThatEqualObjectsHaveSameHashCode();

            AssertHashCodeReturnValueConsistent();
        }
        
        #endregion

        # region Support Methods

        /// <summary>
        /// This test is in no way comprehensive, not suitable for use in a real system.
        /// </summary>
        private void AssertHashCodeReturnValueConsistent()
        {
            TUnderTest firstIdentifier = _createTestObjectCallback();

            for (int i = 0; i < 100; i++)
            {
                Assert.AreEqual(firstIdentifier.GetHashCode(), firstIdentifier.GetHashCode());
            }
        }

        private void AssertThatEqualObjectsHaveSameHashCode()
        {
            TUnderTest firstObject = _createTestObjectCallback();
            TUnderTest secondObject = _createTestObjectCallback();

            Assert.AreEqual(firstObject, secondObject, "The two objects are not equal so the test cannot continue");
            Assert.AreEqual(firstObject.GetHashCode(), secondObject.GetHashCode());
        }

        # endregion
    }
}
